package com.iocup.keybastion.authorize;


import com.iocup.keybastion.authorize.element.AuthElement;
import com.iocup.keybastion.authorize.decision.AuthDecision;
import com.iocup.keybastion.authorize.decision.AuthDecisionFinder;
import com.iocup.keybastion.authorize.element.AuthElementService;
import com.iocup.keybastion.context.WebContextHolder;
import com.iocup.keybastion.exception.AccessDeniedException;
import com.iocup.keybastion.utils.NameUtils;
import com.iocup.keybastion.utils.PathUtil;
import com.pine.sunflower.core.model.CommonErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author xyjxust
 * @create 2022/3/2 11:00
 **/
@Slf4j
public class DefaultAuthorizer implements Authorizer {


    private AuthDecisionFinder finder;

    private AuthElementService authElementService;

    @Override
    public void check(WebContextHolder contextHolder, Method method) {

            //先检查url
            if (check(contextHolder, PathUtil.generateKey(contextHolder.getRequestMethod(), contextHolder.getRequestURL()))) {
                throw new AccessDeniedException(CommonErrorCode.UNAUTHORIZED.getDescription());
            }

            //再检查类上的权限
            if (method != null && check(contextHolder, method.getDeclaringClass().getName())) {
                throw new AccessDeniedException(CommonErrorCode.UNAUTHORIZED.getDescription());
            }

            //再检查方法上的权限
            if (method != null && check(contextHolder, NameUtils.buildMethodFullPath(method))) {
                throw new AccessDeniedException(CommonErrorCode.UNAUTHORIZED.getDescription());
            }


    }


    public DefaultAuthorizer authDecisionFinder(AuthDecisionFinder authDecisionFinder) {
        this.finder = authDecisionFinder;
        return this;
    }

    public DefaultAuthorizer authElementService(AuthElementService authElementService) {
        this.authElementService = authElementService;
        return this;
    }

    private boolean check(WebContextHolder contextHolder, String key) {
        List<AuthElement> elementList = authElementService.getAuthElement(key);
        if (CollectionUtils.isEmpty(elementList)) {
            return false;
        }
        //groupby一把
        Map<String, List<AuthElement>> map = elementList.stream().collect(Collectors.groupingBy(AuthElement::getType));
        //判断指定权限有一个没有通过就表示不通过
        return map.keySet().stream().anyMatch(type -> {
            Optional<AuthDecision> decisionOptional = finder.find(type);
            if (!decisionOptional.isPresent()) {
                log.warn("未找到此类型[{}]的权限决策器，默认不通过", type);
                return true;
            }
            return !decisionOptional.get().decide(map.get(type), contextHolder);
        });
    }
}
